log-drains.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { NextApiRequest, NextApiResponse } from 'next'
  2. import apiWrapper from '@/lib/api/apiWrapper'
  3. import { PROJECT_ANALYTICS_URL } from '@/lib/constants/api'
  4. export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
  5. async function handler(req: NextApiRequest, res: NextApiResponse) {
  6. const { method } = req
  7. const missingEnvVars = envVarsSet()
  8. if (missingEnvVars !== true) {
  9. return res
  10. .status(500)
  11. .json({ error: { message: `${missingEnvVars.join(', ')} env variables are not set` } })
  12. }
  13. const baseUrl = PROJECT_ANALYTICS_URL
  14. if (!baseUrl) {
  15. return res.status(500).json({ error: { message: `LOGFLARE_URL env variable is not set` } })
  16. }
  17. switch (method) {
  18. case 'GET':
  19. // list log drains
  20. const url = new URL(baseUrl)
  21. url.pathname = '/api/backends'
  22. url.search = new URLSearchParams({
  23. 'metadata[type]': 'log-drain',
  24. }).toString()
  25. const upstream = await fetch(url, {
  26. method: 'GET',
  27. headers: {
  28. Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`,
  29. 'Content-Type': 'application/json',
  30. Accept: 'application/json',
  31. },
  32. })
  33. if (!upstream.ok) {
  34. return res
  35. .status(500)
  36. .json({ error: { message: 'Failed to fetch log drains from upstream' } })
  37. }
  38. const resp = await upstream.json()
  39. if (!Array.isArray(resp)) {
  40. return res
  41. .status(500)
  42. .json({ error: { message: 'Unexpected response format from upstream' } })
  43. }
  44. return res.status(200).json(resp)
  45. case 'POST':
  46. // create the log drain
  47. const postUrl = new URL(baseUrl)
  48. postUrl.pathname = '/api/backends'
  49. const postResult = await fetch(postUrl, {
  50. body: JSON.stringify({
  51. ...req.body,
  52. config: req.body.config,
  53. metadata: {
  54. type: 'log-drain',
  55. },
  56. }),
  57. method: 'POST',
  58. headers: {
  59. Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`,
  60. 'Content-Type': 'application/json',
  61. Accept: 'application/json',
  62. },
  63. }).then(async (r) => await r.json())
  64. const sourcesGetUrl = new URL(baseUrl)
  65. sourcesGetUrl.pathname = '/api/sources'
  66. const sources = await fetch(sourcesGetUrl, {
  67. method: 'GET',
  68. headers: {
  69. Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`,
  70. 'Content-Type': 'application/json',
  71. Accept: 'application/json',
  72. },
  73. }).then((r) => r.json())
  74. const params = sources
  75. .filter((source: { name: string; metadata: { type: string } }) =>
  76. [
  77. 'cloudflare.logs.prod',
  78. 'deno-relay-logs',
  79. 'deno-subhosting-events',
  80. 'gotrue.logs.prod',
  81. 'pgbouncer.logs.prod',
  82. 'postgrest.logs.prod',
  83. 'postgres.logs',
  84. 'realtime.logs.prod',
  85. 'storage.logs.prod.2',
  86. ].includes(source.name.toLocaleLowerCase())
  87. )
  88. .map((source: { name: string; id: number }) => {
  89. return { backend_id: postResult.id, lql_string: `~".*?"`, source_id: source.id }
  90. })
  91. const rulesPostUrl = new URL(baseUrl)
  92. rulesPostUrl.pathname = '/api/rules'
  93. await Promise.all(
  94. params.map((param: any) =>
  95. fetch(rulesPostUrl, {
  96. method: 'POST',
  97. body: JSON.stringify(param),
  98. headers: {
  99. Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`,
  100. 'Content-Type': 'application/json',
  101. Accept: 'application/json',
  102. },
  103. })
  104. )
  105. )
  106. return res.status(201).json(postResult)
  107. default:
  108. res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE'])
  109. res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
  110. }
  111. }
  112. const envVarsSet = () => {
  113. const missingEnvVars = [
  114. process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN ? null : 'LOGFLARE_PRIVATE_ACCESS_TOKEN',
  115. process.env.LOGFLARE_URL ? null : 'LOGFLARE_URL',
  116. ].filter((v) => v)
  117. if (missingEnvVars.length == 0) {
  118. return true
  119. } else {
  120. return missingEnvVars
  121. }
  122. }